struct TcFeatureTransformParams
{
    // Grid size for encoding absolute position of sprites
    int absPosGridSize;

    // Grid size for encoding relative position of sprites
    int relPosGridSize;

    // Only sprites with id < _numSprites are considered
    int numSprites;

    // The total number of possible actions
    int numActions;

    int frameHeight;
    int frameWidth;
};


// Produces tile-coded features from frames
class TcFeatureTransform
{
public:
    TcFeatureTransform() {}
    TcFeatureTransform(const TcFeatureTransformParams &params);

    // Get the dimension of this feature transform
    int dimension() const;

    void transform(const Frame &frame, int actionIndex, AbstractIntVector &out) const;

    // Get a description of a feature
    string describeFeature(int index) const;

    // void transform(const Frame &frame, int actionIndex, vector<double> &out) const;

    void transform(const Frame &frame, vector<double> &out) const;

    // void gridWorldTransform(const Frame &frame, int actionIndex, vector<double> &out);

private:
    int numAbsPosHorzBins;
    int numAbsPosVertBins;
    int numAbsPosBins;

    int numRelPosHorzBins;
    int numRelPosVertBins;
    int numRelPosBins;

    int numSpritePairs;
    int actionBlockSize;

    // Gets the index of the start of the features for this action
    int getActionBlockStart(int actionIndex) const;

    // Get the index of the start of the absolute position features for this sprite id
    // (relative to an action block)
    int getAbsPosBlockStart(int spriteId) const;

    // Get the index of the feature encoding an absolute sprite position at the location
    // specified by coord (relative to an absolute position block)
    int getAbsPosBinIndex(const Vec2i &coord) const;

    // Get the index of the feature encoding the absolute position of this sprite
    // (relative to an action block)
    int getAbsPosBinIndex(const SpriteLocation &sprite) const;

    // Get the index associated with a pair of sprite ids. The order is
    // (0, 0), (1, 0), (1, 1), (2, 0), (2, 1), (2, 2), ...
    int getSpritePairIndex(int id1, int id2) const;

    // Get the index of the start of the relative position features for the pair of
    // sprite ids (relative to an action block)
    int getRelPosBlockStart(int id1, int id2) const;

    // Get the the index of the feature encoding the displacement from coord1 to
    // coord1 (relative to a relative position block)
    int getRelPosBinIndex(const Vec2i &coord1, const Vec2i &coord2) const;

    // Get the index of the feature encoding the relative position between two sprites
    // (relative to an action block)
    int getRelPosBinIndex(const SpriteLocation &sprite1, const SpriteLocation &sprite2) const;

    void addAbsPosFeatures(const Frame &frame, vector<double> &out) const;
    void addActionFeatures(const ControllerState &action, vector<double> &out) const;

    TcFeatureTransformParams _params;

    friend OutputDataStream & operator<< (OutputDataStream &os, const TcFeatureTransform &transform);
    friend InputDataStream & operator>> (InputDataStream &is, TcFeatureTransform &transform);
};

OutputDataStream & operator<< (OutputDataStream &os, const TcFeatureTransform &transform);
InputDataStream & operator>> (InputDataStream &is, TcFeatureTransform &transform);